#include "bootpack.h"
#include <string.h>
#include <stdio.h>

void shell_task(struct SHEET *sheet, int memtotal)
{
    int data;
    char cmdline[30];
    struct SHELL shel;
    struct TASK *task = task_now();
    struct FILEHANDLE fhandle[8];
    struct MEMMAN *memman = (struct MEMMAN *)MEMMAN_ADDR;
    int *fat = (int *)memman_alloc_4k(memman, 4 * 2880);
    shel.sht = sheet;
    shel.cur_x = 8;
    shel.cur_y = 28;
    shel.cur_c = -1;
    task->shel = &shel;
    task->cmdline = cmdline;

    if (shel.sht != 0)
    {
        shel.timer = timer_alloc();
        timer_init(shel.timer, &task->fifo, 1);
        timer_settime(shel.timer, 50);
    }
    file_readfat(fat, (unsigned char *)(ADR_DISKIMG + 0x000200));

    for (int i = 0; i < 8; i++)
        fhandle[i].buf = 0;         //未使用
    task->fhandle = fhandle;
    task->fat = fat;

    /* 显示命令提示符 */
    shel_putchar(&shel, '$', 1);

    while (1)
    {
        io_cli();
        if (fifo32_status(&task->fifo) == 0)
        {
            task_sleep(task);
            io_sti();
        }
        else
        {
            data = fifo32_get(&task->fifo);
            io_sti();
            if (data <= 1 && shel.sht != 0) //光标定时器
            {
                if (data != 0)
                {
                    timer_init(shel.timer, &task->fifo, 0);
                    if (shel.cur_c >= 0)
                        shel.cur_c = COL8_FFFFFF;
                }
                else
                {
                    timer_init(shel.timer, &task->fifo, 1);
                    if (shel.cur_c >= 0)
                        shel.cur_c = COL8_000000;
                }
                timer_settime(shel.timer, 50);
            }
            if (data == 2) /* 光标开 */
            {
                shel.cur_c = COL8_FFFFFF;
            }
            if (data == 3) /* 光标关 */
            {
                if (shel.sht != 0)
                {
                    boxfill8(shel.sht->buf, shel.sht->bxsize, COL8_000000, shel.cur_x,
                         shel.cur_y, shel.cur_x + 7, shel.cur_y + 15);
                }
                shel.cur_c = -1;
            }
            if (data == 4) /* 点击窗口的x按钮 */
            {
                cmd_exit(&shel, fat);
            }
            if (256 <= data && data <= 511) //通过task_a传来的键盘数据
            {
                if (data == 8 + 256) /* Backspace */
                {
                    if (shel.cur_x > 16)
                    {
                        shel_putchar(&shel, ' ', 0);
                        shel.cur_x -= 8;
                    }
                }
                else if (data == 10 + 256) /* enter */
                {
                    /* 用空格擦除光标 */
                    shel_putchar(&shel, ' ', 0);
                    cmdline[shel.cur_x / 8 - 2] = 0;
                    shel_newline(&shel);
                    shel_runcmd(cmdline, &shel, fat, memtotal);
                    if (shel.sht == 0)
                        cmd_exit(&shel, fat);
                    /* 显示提示符 */
                    shel_putchar(&shel, '$', 1);
                }
                else
                {
                    if (shel.cur_x < 240)
                    {
                        cmdline[shel.cur_x / 8 - 2] = data - 256;
                        shel_putchar(&shel, data - 256, 1);
                    }
                }
            }
            /* 光标再显示 */
            if (sheet != 0)
            {
                if (shel.cur_c >= 0) 
                {
                    boxfill8(shel.sht->buf, shel.sht->bxsize, shel.cur_c, shel.cur_x, shel.cur_y,
                            shel.cur_x + 7, shel.cur_y + 15);
                }
                sheet_refresh(shel.sht, shel.cur_x, shel.cur_y, shel.cur_x + 8, shel.cur_y + 16);
            }
        }
    }
}

void shel_putchar(struct SHELL *shel, int c, char move)
{
    char s[2];
    s[0] = c;
    s[1] = 0;
    if (s[0] == 0x09) /* 制表符 */
    {
        for (;;)
        {
            if (shel->sht != 0)
                putfonts8_asc_sht(shel->sht, shel->cur_x, shel->cur_y, COL8_FFFFFF, COL8_000000, " ", 1);
            shel->cur_x += 8;
            if (shel->cur_x == 8 + 240) /* 换行 */
                shel_newline(shel);
            if (((shel->cur_x - 8) & 0x1f) == 0) /* tab = 4 */
                break;
        }
    }
    else if (s[0] == 0x0a) /* 换行 */
    {
        shel_newline(shel);
    }
    else if (s[0] == 0x0d)
    {
        /* 目前不做操作 */
    }
    else
    {
        if (shel->sht != 0)
            putfonts8_asc_sht(shel->sht, shel->cur_x, shel->cur_y, COL8_FFFFFF, COL8_000000, s, 1);
        if (move != 0)
        {
            shel->cur_x += 8;
            if (shel->cur_x == 8 + 240) /* 换行 */
            {
                shel_newline(shel);
            }
        }
    }
    return;
}

void shel_newline(struct SHELL *shel)
{
    int x, y;
    struct SHEET *sheet = shel->sht;
    if (shel->cur_y < 28 + 112)
        shel->cur_y += 16; /* 到下一行 */
    else
    {
        /* 滚动窗口 */
        if (sheet != 0)
        {
            for (y = 28; y < 28 + 112; y++)
            {
                for (x = 8; x < 8 + 240; x++)
                    sheet->buf[x + y * sheet->bxsize] = sheet->buf[x + (y + 16) * sheet->bxsize];
            }
            for (y = 28 + 112; y < 28 + 128; y++)
            {
                for (x = 8; x < 8 + 240; x++)
                    sheet->buf[x + y * sheet->bxsize] = COL8_000000;
            }
            sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
        }
    }
    shel->cur_x = 8;
    return;
}

void shel_putstr(struct SHELL *shel, char *s)
{
    for (; *s != 0; s++)
        shel_putchar(shel, *s, 1);
    return;
}

void shel_putstrlen(struct SHELL *shel, char *s, int l)
{
    for (int i = 0; i < l; i++)
        shel_putchar(shel, s[i], 1);
    return;
}

void shel_runcmd(char *cmdline, struct SHELL *shel, int *fat, int memtotal)
{
    /* 运行指令 */
    if (strcmp(cmdline, "mem") == 0 && shel->sht != 0)
    {
        cmd_mem(shel, memtotal);
    }
    else if (strcmp(cmdline, "clear") == 0 && shel->sht != 0)
    {
        cmd_clear(shel);
    }
    else if (strcmp(cmdline, "ls") == 0 && shel->sht != 0)
    {
        cmd_ls(shel);
    }
    else if (strcmp(cmdline, "exit") == 0)
    {
        cmd_exit(shel, fat);
    }
    else if (strncmp(cmdline, "start ", 6) == 0)
    {
        cmd_start(shel, cmdline, memtotal);
    }
   else if (strncmp(cmdline, "nst ", 4) == 0)
    {
       cmd_nst(shel, cmdline, memtotal);
    }
    else if (cmdline[0] != 0)
    {
        if (cmd_runapp(shel, fat, cmdline) == 0)
        {
            /* not commad, not application or space */
            shel_putstr(shel, "Commad not found.\n\n");
        }
    }
    return;
}

void cmd_mem(struct SHELL *shel, int memtotal)
{
    char s[60];
    struct MEMMAN *memman = (struct MEMMAN *)MEMMAN_ADDR;
    sprintf(s, "total   %dMB\nfree %dKB\n\n", memtotal / (1024 * 1024), memman_total(memman) / 1024);
    shel_putstr(shel, s);
    return;
}

void cmd_clear(struct SHELL *shel)
{
    int x, y;
    struct SHEET *sheet = shel->sht;
    for (y = 28; y < 28 + 128; y++)
    {
        for (x = 8; x < 8 + 240; x++)
            sheet->buf[x + y * sheet->bxsize] = COL8_000000;
    }
    sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
    shel->cur_y = 28;
    return;
}

void cmd_ls(struct SHELL *shel)
{
    int i, j;
    char s[30];
    struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG + 0x002600);
    for (i = 0; i < 224; i++)
    {
        if (finfo[i].name[0] == 0x00)
            break;
        if (finfo[i].name[0] != 0xe5)
        {
            if ((finfo[i].type & 0x18) == 0)
            {
                sprintf(s, "filename.ext    %7d\n", finfo[i].size);
                for (j = 0; j < 8; j++)
                    s[j] = finfo[i].name[j];
                s[9] = finfo[i].ext[0];
                s[10] = finfo[i].ext[1];
                s[11] = finfo[i].ext[2];
                shel_putstr(shel, s);
            }
        }
    }
    shel_newline(shel);
    return;
}

void cmd_exit(struct SHELL *shel, int *fat)
{
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = task_now();
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
    timer_cancel(shel->timer);
    memman_free_4k(memman, (int) fat, 4 * 2880);
    io_cli();
    if (shel->sht != 0)
        fifo32_set(fifo, shel->sht - shtctl->sheets0 + 768);    // 768~1023
    else
        fifo32_set(fifo, task - taskctl->tasks + 1024);    // 1024~2023
    io_sti();
    for (;;)
        task_sleep(task);
}

void cmd_start(struct SHELL *shel, char *cmdline, int memtotal)
{
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct SHEET *sht = open_shell(shtctl, memtotal);
    struct FIFO32 *fifo = &sht->task->fifo;
    sheet_slide(sht, 32, 4);
    sheet_updown(sht, shtctl->top);
    /* 将命令行输入的字符复制到新命令行中 */
    for (int i = 6; cmdline[i] != 0; i++)
        fifo32_set(fifo, cmdline[i] + 256);
    fifo32_set(fifo, 10 + 256);     //回车
    shel_newline(shel);
    return;
}

void cmd_nst(struct SHELL *shel, char *cmdline, int memtotal)
{
    struct TASK *task = open_shtask(0, memtotal);
    struct FIFO32 *fifo = &task->fifo;
    /* 将命令行输入的字符复制到新命令行中 */
    for (int i = 4; cmdline[i] != 0; i++)
        fifo32_set(fifo, cmdline[i] + 256);
    fifo32_set(fifo, 10 + 256);     //回车
    shel_newline(shel);
    return;
}

int cmd_runapp(struct SHELL *shel, int *fat, char *cmdline)
/* 启动应用程序 */
{
    int i, segsiz, datsiz, dathrb, esp;
    char name[18], *p, *q;
    struct SHEET *sht;
    struct SHTCTL *shtctl;
    struct FILEINFO *finfo;
    struct TASK *task = task_now();
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

    /* 根据命令生成文件名 */
    for (i = 0; i < 13; i++)
    {
        if (cmdline[i] <= ' ')
            break;
        name[i] = cmdline[i];
    }
    name[i] = 0;

    /* 寻找文件 */
    finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
    if (finfo == 0 && name[i - 1] != '.')
    {
        /* 如果找不到文件,加上后缀名重新查找 */
        name[i + 0] = '.';
        name[i + 1] = 'H';
        name[i + 2] = 'R';
        name[i + 3] = 'B';

        name[i + 4] = 0;
        finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
    }

    if (finfo != 0)
    {
        /* 找到文件 */
        p = (char *) memman_alloc_4k(memman, finfo->size);
        file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
        if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00)
        {
            segsiz = *((int *) (p + 0x0000));
            esp    = *((int *) (p + 0x000c));
            datsiz = *((int *) (p + 0x0010));
            dathrb = *((int *) (p + 0x0014));
            q = (char *) memman_alloc_4k(memman, segsiz);
            task->ds_base = (int) q;
            set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + RING3_PREMIS);
            set_segmdesc(task->ldt + 1, segsiz - 1,      (int) q, AR_DATA32_RW + RING3_PREMIS);
            for (i = 0; i < datsiz; i++)
                q[esp + i] = p[dathrb + i];
            start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
            shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
            for (i = 0; i < MAX_SHEETS; i++)
            {
                sht = &(shtctl->sheets0[i]);
                if ((sht->flags & 0x11) == 0x11 && sht->task == task) //找到应用程序遗留的窗口
                    sheet_free(sht);    //关闭
            }
            for (i = 0; i < 8; i++)     //将未关闭的文件关闭
            {
                if (task->fhandle[i].buf != 0)
                {
                    memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
                    task->fhandle[i].buf = 0;
                }
            }
            timer_cancelall(&task->fifo);
            memman_free_4k(memman, (int) q, segsiz);
        }
        else
            shel_putstr(shel, ".hrb file format error.\n");
        memman_free_4k(memman, (int) p, finfo->size);
        shel_newline(shel);
        /* 找到文件 */
        return 1;
    }
    /* 未找到文件 */
    return  0;
}

int *zic_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
    int i;
    struct TASK *task = task_now();
    struct SHEET *sht;
    struct FILEINFO *finfo;
    struct FILEHANDLE *fh;
    int ds_base = task->ds_base;
    struct SHELL *shel = task->shel;
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct FIFO32 *sys_fifo = (struct FIFO32 *) *((int *) 0x0fec);

    int *reg = &eax + 1;    /* eax后面的值 */
        /* 强行改写通过pushad保存的值 */
        /* reg[0] : edi,    reg[1] : esi,   reg[2] : ebp,   reg[3] : esp */
        /* reg[4] : ebx,    reg[5] : edx,   reg[6] : ecx,   reg[7] : eax */

    if (edx == 1)
    {
        shel_putchar(shel, eax & 0xff, 1);
    }
    else if (edx == 2)
    {
        shel_putstr(shel, (char *) ebx + ds_base);
    }
    else if (edx == 3)
    {
        shel_putstrlen(shel, (char *) ebx + ds_base, ecx);
    }
    else if (edx == 4)
    {
        return &(task->tss.esp0);
    }
    else if (edx == 5)
    {
        sht = sheet_alloc(shtctl);
        sht->task = task;
        sht->flags |= 0x10;
        sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
        make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
        sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2); //窗口初始位置
        sheet_updown(sht, shtctl->top);
        reg[7] = (int) sht;
    }
    else if (edx == 6)
    {
        sht = (struct SHEET *) (ebx & 0xfffffffe);
        putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *) ebp + ds_base);
        if ((ebx & 1) == 0)
            sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);
    }
    else if (edx == 7)
    {
        sht = (struct SHEET *) (ebx & 0xfffffffe);
        boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);
        if ((ebx & 1) == 0)
            sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
    }
    else if (edx == 8)
    {
        sht = (struct SHEET *) ebx;
        sheet_refresh(sht, eax, ecx, esi, edi);
    }
    else if (edx == 9)
    {
        sht = (struct SHEET *) (ebx & 0xfffffffe);
        zic_api_win_line(sht, eax, ecx, esi, edi, ebp);
        if ((ebx & 1) == 0)
        {
            if (eax > esi) {
                i = eax;
                eax = esi;
                esi = i;
            }
            if (ecx > edi) {
                i = ecx;
                ecx = edi;
                edi = i;
            }
            sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
        }
    }
    else if (edx == 10)
    {
        sheet_free((struct SHEET *) ebx);
    }
    else if (edx == 11)
    {
        memman_init((struct MEMMAN *) (ebx + ds_base));
        ecx &= 0xfffffff0;
        memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
    }
    else if (edx == 12)
    {
        ecx = (ecx + 0x0f) & 0xfffffff0;
        reg[7] = memman_alloc((struct MEMMAN *) (ebx + ds_base), ecx);
    }
    else if (edx == 13)
    {
        ecx = (ecx + 0x0f) & 0xfffffff0;
        memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
    }
    else if (edx == 14)
    {
        sht = (struct SHEET *) (ebx & 0xfffffffe);
        sht->buf[sht->bxsize * edi + esi] = eax;
        if ((ebx & 1) == 0)
            sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
    }
    else if (edx == 15)
    {
        for (;;)
        {
            io_cli();
            if (fifo32_status(&task->fifo) == 0)
            {
                if (eax != 0)
                    task_sleep(task);   //fifo为空,休眠并等待
                else
                {
                    io_sti();
                    reg[7] = -1;
                    return 0;
                }
            }
            i = fifo32_get(&task->fifo);
            io_sti();
            if (i <= 1)     //光标用定时器
            {   
                //应用程序不使用光标,所以将下一次显示记录值置为1
                timer_init(shel->timer, &task->fifo, 1);
                timer_settime(shel->timer, 50);
            }
            if (i == 2)     //光标ON
            {
                shel->cur_c = COL8_FFFFFF;
            }
            if (i == 3)     //光标OFF
            {
                shel->cur_c = -1;
            }
            if (i == 4) //只关闭shell窗口
            {
                timer_cancel(shel->timer);
                io_cli();
                fifo32_set(sys_fifo, shel->sht - shtctl->sheets0 + 2024); //2024~2279
                shel->sht = 0;
                io_sti();
            }
            if (i >= 256)
            {
                reg[7] = i - 256;
                return 0;
            }
        }
    }
    else if (edx == 16)
    {
        reg[7] = (int) timer_alloc();
        ((struct TIMER *) reg[7])->flag2 = TIMER_AUTOCANCEL_ON;
    }
    else if (edx == 17)
    {
        timer_init((struct TIMER *) ebx, &task->fifo, eax + 256);
    }
    else if (edx == 18)
    {
        timer_settime((struct TIMER *) ebx, eax);
    }
    else if (edx == 19)
    {
        timer_free((struct TIMER *) ebx);
    }
    else if (edx == 20)
    {
        if (eax == 0)
        {
            i = io_in8(0x61);
            io_out8(0x61, i & 0x0d);
        }
        else
        {
            i = 1193180000 / eax;
            io_out8(0x43, 0xb6);

            io_out8(0x42, i & 0xff);
            io_out8(0x42, i >> 8);
            i = io_in8(0x61);
            io_out8(0x61, (i | 0x03) & 0x0f);
        }
    }
    else if (edx == 21)
    {
        for (i = 0; i < 8; i++)
        {
            if (task->fhandle[i].buf == 0)
                break;
        }
        fh = &task->fhandle[i];
        reg[7] = 0;
        if (i < 8)
        {
            finfo = file_search((char *) ebx + ds_base,
                (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
            if (finfo != 0)
            {
                reg[7] = (int) fh;
                fh->buf = (char *) memman_alloc_4k(memman, finfo->size);
                fh->size = finfo->size;
                file_loadfile(finfo->clustno, finfo->size, fh->buf, task->fat, 
                    (char *) (ADR_DISKIMG + 0x003e00));
            }
        }
    }
    else if (edx == 22)
    {
        fh = (struct FILEHANDLE *) eax;
        memman_free_4k(memman, (int) fh->buf, fh->size);
        fh->buf = 0;
    }
    else if (edx == 23)
    {
        fh = (struct FILEHANDLE *) eax;
        if (ecx == 0)
            fh->pos = ebx;
        else if (ecx == 1)
            fh->pos += ebx;
        else if (ecx == 2)
            fh->pos = fh->size + ebx;
        if (fh->pos < 0)
            fh->pos = 0;
        if (fh->pos > fh->pos)
            fh->pos = fh->size;
    }
    else if (edx == 24)
    {
        fh = (struct FILEHANDLE *) eax;
        if (ecx == 0)
            reg[7] = fh->size;
        else if (ecx == 1)
            reg[7] = fh->pos;
        else if (ecx == 2)
            reg[7] = fh->pos - fh->size;
    }
    else if (edx == 25)
    {
        fh = (struct FILEHANDLE *) eax;
        for (i = 0; i < ecx; i++)
        {
            if (fh->pos == fh->size)
                break;
            *((char *) ebx + ds_base + i) = fh->buf[fh->pos];
            fh->pos++;
        }
        reg[7] = i;
    }
    else if (edx = 26)
    {
        i = 0;
        for (;;)
        {
            *((char *) ebx + ds_base + i) = task->cmdline[i];
            if (task->cmdline[i] == 0)
                break;
            if (i > ecx)
                break;
            i++;
        }
        reg[7] = i;
    }
    return 0;
}

int *inthandler0d(int *esp)
{   
    char s[30];
    struct TASK *task = task_now();
    struct SHELL *shel = task->shel;
    shel_putstr(shel, "\nINT 0D :\n General Protected Exception.\n");
    sprintf(s, "EIP = %08X\n", esp[11]);
    shel_putstr(shel, s);
    return &(task->tss.esp0);   /* 强制结束该程序 */
}

int *inthandler0c(int *esp)
{   
    char s[30];
    struct TASK *task = task_now();
    struct SHELL *shel = task->shel;
    shel_putstr(shel, "\nINT 0C :\n Stack Exception.\n");
    sprintf(s, "EIP = %08X\n", esp[11]);
    shel_putstr(shel, s);
    return &(task->tss.esp0);   /* 强制结束该程序 */
}

void zic_api_win_line(struct SHEET *sht, int x0, int y0, int x1, int y1, int col)
{
    int i, x, y, len, dx, dy;

    dx = x1 - x0;
    dy = y1 - y0;
    x = x0 << 10;
    y = y0 << 10;
    if (dx < 0)
        dx = - dx;
    if (dy < 0)
        dy = -dy;
    if (dx >= dy)
    {
        len = dx + 1;
        if (x0 > x1)
            dx = -1024;
        else
            dx =  1024;
        if (y0 <= y1)
            dy = ((y1 - y0 + 1) << 10) / len;
        else
            dy = ((y1 - y0 - 1) << 10) / len;
    }
    else
    {
        len = dy + 1;
        if (y0 > y1)
            dy = -1024;
        else
            dy =  1024;
        if (x0 <= x1)
            dx = ((x1 - x0 + 1) << 10) / len;
        else
            dx = ((x1 - x0 - 1) << 10) / len;        
    }
    
    for (i = 0; i < len; i++)
    {
        sht->buf[(y >> 10) * sht->bxsize + (x >> 10)] = col;
        x += dx;
        y += dy;
    }

    return;
}
